package com.gwaeron.game;

// TODO:
/*
 * 
 * Right now, the game will for some reason not register if ctrl is held down while changing zones. This means the run button must be pressed on every zone change.
 * 
 */
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.event.KeyEvent;
import java.net.URL;
import java.util.Comparator;
import java.util.StringTokenizer;

// GTGE
import battle.BattleEvent;

import com.golden.gamedev.GameObject;
import com.golden.gamedev.GameEngine;
import com.golden.gamedev.engine.BaseGraphics;
import com.golden.gamedev.engine.audio.MidiRenderer;
import com.golden.gamedev.engine.timer.SystemTimer;
import com.golden.gamedev.object.Sprite;
import com.golden.gamedev.object.PlayField;
import com.golden.gamedev.util.FileUtil;
import com.golden.gamedev.util.ImageUtil;

public class RPGGame extends com.golden.game.GameObject {

	/***************************** GAME STATE **********************************/

	public static final int PLAYING = 0, TALKING = 1, MENU = 2, BATTLE = 3;
	int gameState = PLAYING;

	RPGPlayField playfield;
	Map map;
	ForegroundSprite[][] foreground;
	RPGSprite hero;

	RPGDialog dialog;
	RPGMenu menu;
	BattleEvent battle;
	MusicPlayer musicPlayer;

	NPC talkToNPC; // the NPC we talk to
	int talkToNPCDirection; // old NPC direction before
							// we talk to him/her

	/****************************************************************************/
	/******************************* CONSTRUCTOR ********************************/
	/****************************************************************************/

	public RPGGame(com.golden.game.GameEngine parent) {
		super(parent);
	}

	public void initResources() {
		this.musicPlayer = MusicPlayer.get();
		//Load the first map...
		musicPlayer.play("C:\\Users\\Robin\\Desktop\\karate.mp3");
		map = new Map(bsLoader, bsIO, "xidan");
		playfield = new RPGPlayField(map);
		playfield.setComparator(new Comparator() {
			public int compare(Object o1, Object o2) {
				// sort based on y-order
				if (o1 instanceof ForegroundSprite) {
					return 1;
				}
				if (o2 instanceof ForegroundSprite) {
					return -1;
				}
				return (int) (((Sprite) o1).getY() - ((Sprite) o2).getY());
			}
		});
		hero = new RPGSprite(this, getImages("Chara1.png", 3, 4), 10, 10, 4,
				RPGSprite.UP);
		playfield.add(hero);

		this.foreground = map.getForeground();
		for (int i = 0; i < foreground.length; i++) {
			for (int j = 0; j < foreground[0].length; j++) {
				if (foreground[i][j] != null)
					playfield.add(foreground[i][j]); // This is for getting the
														// objects that are
														// shown "above" the
														// characters and other
														// sprites.
			}
		}

		String[] event = FileUtil.fileRead(bsIO.getStream("xidan.evt"));
		LogicUpdater stayStill = new StayStill();
		LogicUpdater randomMovement = new RandomMovement();
		LogicUpdater cycleUpDown = new CycleUpDown();
		LogicUpdater cycleLeftRight = new CycleLeftRight();
		for (int i = 0; i < event.length; i++) {
			if (event[i].startsWith("#") == false) {
				StringTokenizer token = new StringTokenizer(event[i], ",");
				String type = token.nextToken();
				String image = token.nextToken();
				String portrait = token.nextToken();
				int posx = Integer.parseInt(token.nextToken());
				int posy = Integer.parseInt(token.nextToken());
				int direction = Integer.parseInt(token.nextToken());
				int speed = Integer.parseInt(token.nextToken());
				int frequence = Integer.parseInt(token.nextToken());

				String logicUpdater = token.nextToken();
				LogicUpdater logic = stayStill;
				if (logicUpdater.equals("random")) {
					logic = randomMovement;
				} else if (logicUpdater.equals("updown")) {
					logic = cycleUpDown;
				} else if (logicUpdater.equals("leftright")) {
					logic = cycleLeftRight;
				}

				String[] dialogNPC = null;
				if (token.hasMoreTokens()) {
					StringTokenizer dialogToken = new StringTokenizer(
							event[i].substring(StringHandler.nthOccurrence(
									event[i], ',', 8) + 1), "\\");
					dialogNPC = new String[dialogToken.countTokens()];
					for (int j = 0; j < dialogNPC.length; j++) {
						dialogNPC[j] = dialogToken.nextToken();
					}
				}

				BufferedImage[] npcImage = null;
				if (image.equals("none") == false) {
					npcImage = getImages(""+image, 3, 4);
				}
				BufferedImage npcPortrait = null;
				if(!portrait.equals("none")) {
					npcPortrait = getImage(""+portrait);
				}

				RPGSprite npc = new NPC(this, npcImage, posx, posy, speed,
						direction, frequence, logic, dialogNPC, npcPortrait);
				if (type.equals("stepping")) {
					npc.setAnimate(true);
					npc.setLoopAnim(true);
				}

				playfield.add(npc);
			}
		}

		// menu = new RPGMenu();
		dialog = new RPGDialog(
				fontManager.getFont(getImage("font.png")), getImage("DialogBoxOpac.png", false), getImage("DialogArrow.png"));
		menu = new RPGMenu(
				fontManager.getFont(getImage("font.png")), getImage("menubox.png", false), getImage("DialogArrow.png"));
		map.setToCenter(hero);
		battle = new BattleEvent();
		fadeIn(); 
	}

	public void update(long elapsedTime) {
		// System.out.println(bsInput.isKeyDown(KeyEvent.VK_CONTROL));
		playfield.update(elapsedTime);
		if(!keyDown(KeyEvent.VK_CONTROL))hero.toggleRun(false); //if CTRL is not held down, don't run.
		switch (gameState) {

		//In battle. Will probably be lots of stuff in here later.
		case BATTLE:
			if (battle.endBattle) {
				gameState = PLAYING;
			}
			if(bsInput.isKeyPressed(KeyEvent.VK_B)){
				battle.endBattle = true;
			}
		
			break;
		// In menu.
		// Move cursor, enter submenus.
		case MENU:
			if (bsInput.isKeyPressed(KeyEvent.VK_ESCAPE)) {

				fadeOut();
				gameState = PLAYING;
				fadeIn();
			}
			break;

		// playing
		// moving hero : arrow key
		// talk to npc : space
		case PLAYING:
			if (bsInput.isKeyDown(KeyEvent.VK_CONTROL)) {
				hero.toggleRun(true);
				// System.out.println("Run toggled");
			}
			if (bsInput.isKeyPressed(KeyEvent.VK_ESCAPE)) {

				fadeOut();
				gameState = MENU;
				fadeIn();
			} else if(bsInput.isKeyPressed(KeyEvent.VK_B)){
				gameState=BATTLE;
				battle.endBattle = false;
			}
			
			if (hero.getStatus() == RPGSprite.STANDING) {
	
				if (keyDown(KeyEvent.VK_LEFT)) {
					hero.walkTo(RPGSprite.LEFT, -1, 0);

				} else if (keyDown(KeyEvent.VK_RIGHT)) {
					hero.walkTo(RPGSprite.RIGHT, 1, 0);

				} else if (keyDown(KeyEvent.VK_UP)) {
					hero.walkTo(RPGSprite.UP, 0, -1);

				} else if (keyDown(KeyEvent.VK_DOWN)) {
					hero.walkTo(RPGSprite.DOWN, 0, 1);

				}

				// action key
				if (keyPressed(KeyEvent.VK_SPACE)) {
					int targetX = hero.tileX, targetY = hero.tileY;
					switch (hero.getDirection()) {
					case RPGSprite.LEFT:
						targetX = hero.tileX - 1;
						break;
					case RPGSprite.RIGHT:
						targetX = hero.tileX + 1;
						break;
					case RPGSprite.UP:
						targetY = hero.tileY - 1;
						break;
					case RPGSprite.DOWN:
						targetY = hero.tileY + 1;
						break;
					}

					talkToNPC = (NPC) map.getLayer3(targetX, targetY);

					if (talkToNPC != null && talkToNPC.dialog != null) {
						dialog.setDialog(bsIO, talkToNPC.dialog, (hero.getScreenY()
								+ hero.getHeight() < 340), talkToNPC.portrait);

						// make NPC and hero, face to face!
						// we store the old NPC direction first
						talkToNPCDirection = talkToNPC.getDirection();
						switch (hero.getDirection()) {
						case RPGSprite.LEFT:
							talkToNPC.setDirection(RPGSprite.RIGHT);
							break;
						case RPGSprite.RIGHT:
							talkToNPC.setDirection(RPGSprite.LEFT);
							break;
						case RPGSprite.UP:
							talkToNPC.setDirection(RPGSprite.DOWN);
							break;
						case RPGSprite.DOWN:
							talkToNPC.setDirection(RPGSprite.UP);
							break;
						}

						gameState = TALKING;
					}
				}

				// quit key
				if (keyPressed(KeyEvent.VK_Q)) {
					// parent.nextGameID = DemoRPG.TITLE;
					finish();
				}
			}
			
			break;

		// talking to npc, end when Z or X or ESC is pressed
		case TALKING:
			if(keyPressed(KeyEvent.VK_SHIFT)){
				dialog.setBottom(!dialog.bottom);
			}
			if (dialog.endDialog) {
				if (keyPressed(KeyEvent.VK_SPACE) || keyPressed(KeyEvent.VK_X)
						|| keyPressed(KeyEvent.VK_ESCAPE)) {
					// back to old direction
					if(!dialog.next()){
					talkToNPC.setDirection(talkToNPCDirection);
					gameState = PLAYING;
					}
				}
			} else{
				if(keyPressed(KeyEvent.VK_SPACE) || keyPressed(KeyEvent.VK_X) || keyPressed(KeyEvent.VK_ESCAPE)){
					if(dialog.endDialog){
						dialog.next();
					}
					else dialog.endDialog=true;
				}
			}

			dialog.update(elapsedTime);
			break;
		}

		//If we zone: TODO: Must work generally
		map.setToCenter(hero);
		if (map.isZoneEdge(hero.tileX, hero.tileY)) {
			// change maps... Do some fading animation or whatnot
			fadeOut();
			playfield.clearPlayField();
			musicPlayer.play("C:\\Users\\Robin\\Desktop\\other.mp3"); // TODO:
																		// Change.
			this.map = new Map(bsLoader, bsIO, map.nextZone(hero.tileX,
					hero.tileY));
			playfield = new RPGPlayField(map);
			hero = new RPGSprite(this, getImages("Chara1.png", 3, 4), 20, 23,
					3, RPGSprite.RIGHT);
			playfield.add(hero);

			// Do foreground stuff:
			this.foreground = map.getForeground();
			for (int i = 0; i < foreground.length; i++) {
				for (int j = 0; j < foreground[0].length; j++) {
					if (foreground[i][j] != null)
						playfield.add(foreground[i][j]); 
				}
			}
			map.setToCenter(hero);
			fadeIn();
		}

	}

	public void render(Graphics2D g) {
		if(gameState!=BATTLE)playfield.render(g); //the if saves some calculation capacity here.

		if (gameState == TALKING) {
			dialog.render(g);
		} else if (gameState == MENU) {
			menu.render(g);
		} else if (gameState == BATTLE){
			battle.render(g);
		}
	}

	// TODO: Give an argument (in milliseconds) for how long the //fade effect
	// should be.
	public void fadeIn() {
		SystemTimer dummyTimer = new SystemTimer();
		dummyTimer.setFPS(20);
		this.bsInput.refresh();
		float alpha = 1.0f;
		BufferedImage logo = null;
		try {
			URL logoURL = com.gwaeron.game.RPGGame.class
					.getResource("black.png");
			BufferedImage orig = ImageUtil.getImage(logoURL);
			logo = ImageUtil.resize(orig, this.getWidth(), this.getHeight());

			orig.flush();
			orig = null;
		} catch (Exception e) {
		}

		while (alpha > 0.0f) {

			do {
				Graphics2D g = this.bsGraphics.getBackBuffer();
				render(g); // Draw the underlying map
				Composite old = g.getComposite();
				g.setComposite(AlphaComposite.getInstance(
						AlphaComposite.SRC_OVER, alpha));
				g.drawImage(logo, 0, 0, null); // Cover the map with a black
												// image which //fades away
				g.setComposite(old);

			} while (this.bsGraphics.flip() == false);

			long elapsedTime = dummyTimer.sleep();
			double decrement = 0.00055 * elapsedTime;
			if (decrement > 0.15) {
				decrement = 0.15 + ((decrement - 0.04) / 2);
			}
			// alpha -= decrement;

			alpha -= 0.01; // Let's just use this for now... TODO
		}

		logo.flush();
		logo = null;
		dummyTimer.stopTimer();

	}

	// TODO: Give an argument (in milliseconds) for how long the //fade effect
	// should be.
	public void fadeOut() {
		SystemTimer dummyTimer = new SystemTimer();
		dummyTimer.setFPS(20);
		this.bsInput.refresh();

		// loading GTGE logo for splash screen
		BufferedImage logo = null;
		try {
			URL logoURL = com.gwaeron.game.RPGGame.class
					.getResource("black.png");
			BufferedImage orig = ImageUtil.getImage(logoURL);
			// BufferedImage back = //Maybe if we try to save the old image for
			// now?
			logo = ImageUtil.resize(orig, this.getWidth(), this.getHeight());

			orig.flush();
			orig = null;
		} catch (Exception e) {
		}

		this.bsInput.refresh();
		// gradually show (alpha blending)
		float alpha = 0.0f;
		dummyTimer.startTimer();
		boolean firstTime = true;
		while (alpha < 1.0f) {
		
			do {
				Graphics2D g = this.bsGraphics.getBackBuffer();
				Composite old = g.getComposite();
				g.setComposite(AlphaComposite.getInstance(
						AlphaComposite.SRC_OVER, alpha));
				g.drawImage(logo, 0, 0, null);
				g.setComposite(old);
			} while (this.bsGraphics.flip() == false);

			if (firstTime) {
				// workaround for OpenGL mode
				firstTime = false;
				dummyTimer.refresh();
			}

			long elapsedTime = dummyTimer.sleep();
			// double increment = 0.00065 * elapsedTime;
			double increment = 0.01000 * elapsedTime;
			if (increment > 0.22) {
				increment = 0.22 + (increment / 6);
			}
			alpha += increment;

		}
	}	
	public void openMenu(){
		fadeOut();
	}
}